package edu.purdue.cs.ds.vss;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;

public class Generator {
	
	private BigInteger secret;
	private BigInteger random;
	private int l;
	
	public Generator(BigInteger s, BigInteger r, int l) {
		
		this.secret = s.mod(Constants.p);
		this.random = r.mod(Constants.p);
		this.l = l;		
	}
	
	public VerifiableSharing generateVerifiableSharing() {
		
		SecureRandom sr = new SecureRandom();
		
		//Generate S
		ArrayList<BigInteger> shares = new ArrayList<BigInteger>();
		BigInteger sumS = null;
		for (int i = 0; i < this.l-1; i++) {
			BigInteger tmp = new BigInteger(Constants.q.bitLength() - 1, sr);
			shares.add(tmp);
			if(sumS == null) {
				sumS = tmp;
			} else {
				sumS = sumS.add(tmp).mod(Constants.q);
			}
		}
		//The set must add up to r
		shares.add(this.secret.subtract(sumS).mod(Constants.q));

		
		//Generate R
		//This is a random set of elements chosen from Z_q
		ArrayList<BigInteger> randoms = new ArrayList<BigInteger>();
		BigInteger sumR = null;
		for (int i = 0; i < this.l-1; i++) {
			BigInteger tmp = new BigInteger(Constants.q.bitLength() - 1, sr);
			randoms.add(tmp);
			if(sumR == null) {
				sumR = tmp;
			} else {
				sumR = sumR.add(tmp).mod(Constants.q);
			}
		}
		//The set must add up to r
		randoms.add(this.random.subtract(sumR).mod(Constants.q));
		
		//Generate A

		BigInteger a0 = pedersenCommit(this.secret, this.random);

		ArrayList<BigInteger> aList = new ArrayList<BigInteger>();
		aList.add(a0);
		for(int i = 0; i < this.l; i++) {
			BigInteger si = shares.get(i);
			BigInteger ri = randoms.get(i);
			aList.add(pedersenCommit(si, ri));
		}
		
		return new VerifiableSharing(shares, randoms, aList, a0);
	}
	
	public static BigInteger pedersenCommit(BigInteger m, BigInteger r) {
		
		if(m == null || r == null){
			System.out.println("!!!*** * * * * * * * * * * M or R is NULL ***!!!");
			if(m == null)
				System.out.println("S Value is Null");
			else
				System.out.println("R Value is Null");
			return BigInteger.ONE;
		}
		
		m = m.mod(Constants.q);
		r = r.mod(Constants.q);
		BigInteger t1 = Constants.g.modPow(m, Constants.p);
		BigInteger t2 = Constants.h.modPow(r, Constants.p);
		return t1.multiply(t2).mod(Constants.p);
	}

	
}
